import wx
from Buffers import buffers


predefined = (
				("Sobel", (
						(-1, 0, 1),
						(-2, 0, 2),
						(-1, 0, 1)
					)
				),
				("Prewitt", (
						(-1, 0, 1),
						(-1, 0, 1),
						(-1, 0, 1)
					)
				),
				("Mean", (
						(1, 1, 1),
						(1, 1, 1),
						(1, 1, 1)
						)
				)
			)

gaussian_filter = (
					(1, 4, 7, 4, 1),
					(4, 16, 26, 16, 4),
					(7, 26, 41, 26, 7),
					(4, 16, 26, 16, 4),
					(1, 4, 7, 4, 1),
				)


class ConvolutionFrame(wx.Frame):
	def __init__(self, parent, id, title, pos):
		wx.Frame.__init__(self, parent, id, title, pos, style=wx.FRAME_NO_TASKBAR)
		panel = wx.Panel(self, wx.ID_ANY)
		self.vals_ctrl = []
		for i in range(9):
			self.vals_ctrl.append(wx.TextCtrl(panel, wx.ID_ANY, size=(45, -1)))

		predefined_buttons = []
		for data in predefined:
			button = wx.Button(panel, wx.ID_ANY, data[0], style=wx.BU_EXACTFIT)
			self.Bind(wx.EVT_BUTTON, self.OnPredefined, button)
			predefined_buttons.append(button)

		button_convolution = wx.Button(panel, wx.ID_ANY, "Convolution", style=wx.BU_EXACTFIT)
		button_median = wx.Button(panel, wx.ID_ANY, "Median", style=wx.BU_EXACTFIT)
		button_gaussian = wx.Button(panel, wx.ID_ANY, "Gaussian", style=wx.BU_EXACTFIT)
		button_cancel = wx.Button(panel, wx.ID_ANY, "Cancel", style=wx.BU_EXACTFIT)
		self.Bind(wx.EVT_BUTTON, self.OnConvolution, button_convolution)
		self.Bind(wx.EVT_BUTTON, self.OnMedian, button_median)
		self.Bind(wx.EVT_BUTTON, self.OnGaussian, button_gaussian)
		self.Bind(wx.EVT_BUTTON, self.OnCancel, button_cancel)

		sizer_grid = wx.GridSizer(rows=3, cols=3, hgap=2, vgap=2)
		for val_ctrl in self.vals_ctrl:
			sizer_grid.Add(val_ctrl)

		sizer_predefined = wx.BoxSizer(wx.VERTICAL)
		for button in predefined_buttons:
			sizer_predefined.Add(button)

		sizer_top = wx.BoxSizer(wx.HORIZONTAL)
		sizer_top.AddSpacer(20)
		sizer_top.Add(sizer_grid, flag=wx.ALIGN_CENTER)
		sizer_top.AddSpacer(20)
		sizer_top.Add(sizer_predefined, flag=wx.ALIGN_RIGHT)

		sizer_bottom = wx.BoxSizer(wx.HORIZONTAL)
		sizer_bottom.Add(button_cancel)
		sizer_bottom.Add(button_median)
		sizer_bottom.Add(button_gaussian)
		sizer_bottom.Add(button_convolution)

		sizer = wx.BoxSizer(wx.VERTICAL)
		sizer.Add(sizer_top)
		sizer.Add(sizer_bottom, flag=wx.ALIGN_CENTER_HORIZONTAL)
		sizer.Fit(self)
		
		panel.SetSizer(sizer)
		self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

	def OnPredefined(self, event):
		button = event.GetEventObject()
		label = button.GetLabel()

		for item in predefined:
			if item[0] == label:
				matrix =  item[1]
				break

		vals = []
		for row in matrix:
			vals.extend(row)

		for i in range(9):
			self.vals_ctrl[i].SetValue(str(vals[i]))

	def OnConvolution(self, event):
		filter = ([], [], [])
		for i in range(9):
			val = eval(self.vals_ctrl[i].GetValue())
			r = i / 3
			filter[r].append(val)
			
		image = self.convolution(filter)

		new_name = self.source_name + " - convolution"
		name = buffers.add(new_name, image, self.source_name)
		self.displayBuffer(name)
		self.Hide()
	
	def OnMedian(self, event):
		w, h = (self.source_image.GetWidth(), self.source_image.GetHeight())
		image = wx.EmptyImage(w-2, h-2)
		for x in range(w-2):
			for y in range(h-2):
				vals = []
				for i in range(-1, 2):
					for j in range(-1, 2):
						a, b = (x+i, y+j)
						vals.append(self.source_image.GetRed(a, b))
				vals.sort()
				median = vals[4]
				image.SetRGB(x, y, median, median, median)

		new_name = self.source_name + " - median"
		name = buffers.add(new_name, image, self.source_name)
		self.displayBuffer(name)
		self.Hide()
	
	def OnGaussian(self, event):
		filter = self.flip_filter(gaussian_filter)
		image = self.convolution(filter)

		new_name = self.source_name + " - gaussian"
		name = buffers.add(new_name, image, self.source_name)
		self.displayBuffer(name)
		self.Hide()

	def OnCancel(self, event):
		self.Hide()
	
	def OnCloseWindow(self, event):
		self.Hide()
	
	def displayBuffer(self, name):
		self.GetParent().displayBuffer(name)
	
	def set_source(self, source_name, source_image):
		self.source_name = source_name
		self.source_image = source_image.ConvertToGreyscale()

	def convolution(self, filter):
		filter = self.flip_filter(filter)
		gray_source = self.source_image
		w, h = (gray_source.GetWidth(), gray_source.GetHeight())
		filter_dim = len(filter)

		filter = self.deep_list(filter)
		sum = float(self.filter_sum(filter))
		if sum == 0:
			sum = 1
		for i in range(filter_dim):
			for j in range(filter_dim):
				filter[i][j] = filter[i][j] / sum

		diff = filter_dim - 1
		filter_from = - (filter_dim / 2)
		filter_to = filter_dim / 2 + 1

		image = wx.EmptyImage(w-diff, h-diff)
		for x in range(w-diff):
			for y in range(h-diff):
				val = 0
				for i in range(filter_from, filter_to):
					for j in range(filter_from, filter_to):
						a, b = (x+i, y+j)
						gray = gray_source.GetRed(a, b)
						val += gray * filter[i+1][j+1]
				if val > 255:
					val = 255 
				elif val < 0:
					val = 0
				val = int(val)
				image.SetRGB(x, y, val, val, val)
		return image

	def filter_sum(self, filter):
		filter_sum = 0
		for row in filter:
			filter_sum += sum(row)
		return filter_sum

	def deep_list(self, x):
		if type(x) != type(()):
			return x
		return map(self.deep_list, x)

	def flip_filter(self, filter):
		new_filter = []
		for r in range(len(filter)-1, -1, -1):
			row = filter[r]
			tmp_row = []
			for c in range(len(row)-1, -1, -1):
				tmp_row.append(row[c])
			new_filter.append(tmp_row)
		return new_filter
